package cache

import (
	"fmt"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
)

// Cache ⽀持过期时间和最⼤内存⼤⼩的的内存缓存库。 支持数据类型：baseTypes、map、slice、struct及其指针
type Cache interface {
	// SetMaxMemory size 是⼀个字符串。⽀持以下参数: 1KB，100KB，1MB，2MB，1GB 等
	// 此大小仅包含数据大小，不包含cache本身的内存占用（*读写数据时存在copy操作，将会alloc额外空间）
	SetMaxMemory(size string) bool
	// Set 设置⼀个缓存项，并且在expire时间之后过期，0 = 永不过期
	Set(key string, val interface{}, exp time.Duration) bool
	// Get 获取⼀个值
	Get(key string) (interface{}, bool)
	// Del 删除⼀个值
	Del(key string) bool
	// Exists 检测Key是否存在
	Exists(key string) bool
	// Keys 返回已存储的Key个数
	Keys() int
	// Flush 清空所有数据
	Flush()
}

type entry struct {
	data interface{}
	size int64
	hasExp bool
}

type cache struct {
	entries map[string]entry
	mapLock sync.Mutex
	expires []*expire
	expLock sync.Mutex
	sizeMax int64
	sizeCur int64
	update chan int
}

var unitMap = map[string]int64{
	"B": 1,
	"KB": 1 << 10,
	"MB": 1 << 20,
	"GB": 1 << 30,
}

func NewCache() Cache {
	c := &cache{
		entries: map[string]entry{},
		update: make(chan int),
		sizeMax: -1,
	}
	// 异步定时器，使用sort快排+time.Timer为最近过期元素计时
	go func() {
		for {
			sort.Sort(byTime(c.expires))
			var timer *time.Timer
			// 如果没有过期元素，睡上一天
			if len(c.expires) == 0 {
				timer = time.NewTimer(24 * time.Hour)
			} else {
				timer = time.NewTimer(c.expires[0].expire.Sub(time.Now()))
			}
			select {
			// 定时器触发，删除到期元素
			case <- timer.C:
				c.mapLock.Lock()
				c.expLock.Lock()
				var expLst int // 若同时有多个元素到期，同时从expires中移除
				for i, e := range c.expires {
					if e.expire.Before(time.Now()) {
						delete(c.entries, e.key)
						expLst = i
					} else {
						break
					}
				}
				c.expires = c.expires[expLst+1:]
				c.expLock.Unlock()
				c.mapLock.Unlock()
				timer.Stop()
			case <- c.update:
				timer.Stop()
			}
		}
	}()
	return c
}

func (c *cache) SetMaxMemory(size string) bool {
	// 正则分离Num和Uint
	texts := regexp.MustCompile(`\d+|([KMG]?)B`).FindAllString(strings.ToUpper(size), -1)
	num, err := strconv.ParseInt(texts[0], 10, 64)
	if num == 0 || err != nil {
		_ = fmt.Errorf("invalid number or 0: %v\n", texts[0])
		return false
	}
	unit, ok := unitMap[texts[1]]
	if !ok {
		_ = fmt.Errorf("invalid unit: %v\n", texts[1])
		return false
	}
	// 计算目标大小并检查
	sizeTar := num * unit
	if c.sizeCur > sizeTar {
		_ = fmt.Errorf("exceeds current size: %v\n", sizeTar)
		return false
	}
	c.sizeMax = sizeTar
	return true
}

func (c *cache) Set(key string, val interface{}, exp time.Duration) bool {
	c.mapLock.Lock()
	defer c.mapLock.Unlock()
	// 检查是否存在旧数据
	origin, hasOri := c.entries[key]
	// 复制数据并计算大小
	cpy, size := copyAndSize(val)
	if size == 0 {
		_ = fmt.Errorf("empty value: %v", key)
		return false
	}
	// 计算插入后大小
	sizeTar := c.sizeCur + size
	if hasOri {
		sizeTar -= origin.size
	}
	if c.sizeMax != -1 && sizeTar > c.sizeMax {
		_ = fmt.Errorf("exceeded max memory: %v", key)
		return false
	}
	c.sizeCur = sizeTar
	c.entries[key] = entry{
		data: cpy,
		size: size,
		hasExp: exp > 0,
	}
	// 添加过期时间
	var update bool
	c.expLock.Lock()
	if hasOri && origin.hasExp && exp > 0 {
		for _, e := range c.expires {
			if e.key == key {
				e.expire = time.Now().Add(exp)
				update = true
				break
			}
		}
	} else if hasOri && origin.hasExp && exp <= 0 {
		for i, e := range c.expires {
			if e.key == key {
				c.expires = append(c.expires[:i], c.expires[i+1:]...)
				update = true
				break
			}
		}
	} else if exp > 0 {
		c.expires = append(c.expires, &expire{key: key, expire: time.Now().Add(exp)})
		update = true
	}
	c.expLock.Unlock()
	if update {
		c.update <- 0
	}
	return true
}

// 对数据进行复制，避免发生缓存外修改
func copyAndSize(val interface{}) (interface{}, int64) {
	var size int64
	t := reflect.TypeOf(val)
	switch t.Kind() {
	// 引用类型
	case reflect.Map:
		newMap := reflect.MakeMap(t)
		keys := reflect.ValueOf(val).MapRange()
		for keys.Next() {
			newVal, s := copyAndSize(keys.Value().Interface())
			key := keys.Key()
			newMap.SetMapIndex(key, reflect.ValueOf(newVal))
			// 加上Key和Val的大小
			size += int64(key.Type().Size()) + s
		}
		val = newMap.Interface()
		// 加上Map指针的大小
		size += int64(t.Size())
	case reflect.Slice:
		oldSli := reflect.ValueOf(val)
		newSli := reflect.MakeSlice(t, oldSli.Len(), oldSli.Len())
		reflect.Copy(newSli, oldSli)
		if t.Elem().Kind() == reflect.Pointer {
			for i := 0; i < oldSli.Len(); i++ {
				if !oldSli.Index(i).IsZero() {
					newVal, s := copyAndSize(oldSli.Index(i).Interface())
					newSli.Index(i).Set(reflect.ValueOf(newVal))
					size += s
				}
			}
		} else {
			// 加上数据总大小
			size += int64(t.Elem().Size()) * int64(newSli.Len())
		}
		val = newSli.Interface()
		// 加上切片指针的大小
		size += int64(t.Size())
	case reflect.Array:
		oldArr := reflect.ValueOf(val)
		newArr := reflect.New(t).Elem()
		reflect.Copy(newArr, oldArr)
		if t.Elem().Kind() == reflect.Pointer {
			for i := 0; i < oldArr.Len(); i++ {
				if !oldArr.Index(i).IsZero() {
					newVal, s := copyAndSize(oldArr.Index(i).Interface())
					newArr.Index(i).Set(reflect.ValueOf(newVal))
					size += s
				}
			}
		}
		val = newArr.Interface()
		// 加上数组总大小
		size += int64(t.Elem().Size()) * int64(newArr.Len())
	case reflect.Pointer:
		oldVal := reflect.ValueOf(val).Elem()
		cpy, s := copyAndSize(oldVal.Interface())
		newVal := reflect.New(t.Elem())
		newVal.Elem().Set(reflect.ValueOf(cpy))
		val = newVal.Interface()
		size += int64(t.Size()) + s
	case reflect.Bool,
		reflect.Int,
		reflect.Int8,
		reflect.Int16,
		reflect.Int32,
		reflect.Int64,
		reflect.Uint,
		reflect.Uint8,
		reflect.Uint16,
		reflect.Uint32,
		reflect.Uint64,
		reflect.Uintptr,
		reflect.Float32,
		reflect.Float64,
		reflect.Complex64,
		reflect.Complex128,
		reflect.String,
		reflect.Struct:
			size += int64(t.Size())
	}
	return val, size
}

func (c *cache) Get(key string) (interface{}, bool) {
	c.mapLock.Lock()
	defer c.mapLock.Unlock()
	e, ok := c.entries[key]
	if ok {
		val, _ := copyAndSize(e.data)
		return val, true
	}
	return nil, false
}

func (c *cache) Del(key string) bool {
	c.mapLock.Lock()
	defer c.mapLock.Unlock()
	val, ok := c.entries[key]
	if ok {
		delete(c.entries, key)
		if val.hasExp {
			for i, e := range c.expires {
				if e.key == key {
					c.expLock.Lock()
					c.expires = append(c.expires[:i], c.expires[i+1:]...)
					c.expLock.Unlock()
					c.update <- 0
					break
				}
			}
		}
	}
	return ok
}

func (c *cache) Exists(key string) bool {
	c.mapLock.Lock()
	defer c.mapLock.Unlock()
	_, ok := c.entries[key]
	return ok
}

func (c *cache) Flush() {
	c.mapLock.Lock()
	defer c.mapLock.Unlock()
	// 清空存储
	for k := range c.entries {
		delete(c.entries, k)
	}
	// 清空过期时间
	c.expLock.Lock()
	c.expires = c.expires[0:0]
	c.expLock.Unlock()
	c.update <- 0
}

func (c *cache) Keys() int {
	return len(c.entries)
}
